/**
 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

// Example of reading/writing an external serial flash using the PL022 SPI interface

#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"

#include "wizchip_conf.h"
#include "w5100s.h"
#include "dhcp.h"
#include "time.h"

#define UART_ID uart0
#define BAUD_RATE 115200


#define ETHERNET_BUF_MAX_SIZE (1024 * 2)
#define SIP_ID spi0
/* Socket */
#define SOCKET_DHCP 0
/* Retry count */
#define DHCP_RETRY_COUNT 5
#define DNS_RETRY_COUNT 5

#define SPI_SCK_PIN 18
#define SPI_MOSI_PIN 19
#define SPI_MISO_PIN 16
#define SPI_MISO_CS 17
#define SPI_MISO_RST 20


uint8_t wizchip_version = 0x00;
    wiz_NetInfo net_info = {
        .mac = {0x00, 0x08, 0xdc, 0x1e, 0xed, 0x2e},
        .ip = {192, 168, 1, 235},
        .sn = {255, 255, 255, 0},
        .gw = {192, 168, 1, 1},
        .dhcp = NETINFO_DHCP                         // DHCP enable/disable
    };
static uint8_t g_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
    0,
};


/* Timer */
static volatile uint16_t g_msec_cnt = 0;

void wizchip_cs_select_cbfunc(void)
{
    asm volatile("nop \n nop \n nop"); // FIXME
    gpio_put(SPI_MISO_CS, 0);
    asm volatile("nop \n nop \n nop"); // FIXME
}

void wizchip_cs_deselect_cbfunc(void)
{
    asm volatile("nop \n nop \n nop"); // FIXME
    gpio_put(SPI_MISO_CS, 1);
    asm volatile("nop \n nop \n nop"); // FIXME
}
uint8_t wiznet_spi_rb_cbfunc(void)
{
    uint8_t tx_data = 0x00;
    uint8_t rx_data;
    spi_read_blocking(SIP_ID, tx_data, &rx_data, 1);
    return rx_data;
}

void wizchip_spi_wb_cbfunc(uint8_t wb)
{
    spi_write_blocking(SIP_ID, &wb, 1);
}
void gpio_config()
{
    gpio_set_function(SPI_SCK_PIN, GPIO_FUNC_SPI);
    gpio_set_function(SPI_MOSI_PIN, GPIO_FUNC_SPI);
    gpio_set_function(SPI_MISO_PIN, GPIO_FUNC_SPI);
    // Make the SPI pins available to picotool
    bi_decl(bi_3pins_with_func(SPI_MISO_PIN, SPI_MOSI_PIN, SPI_SCK_PIN, GPIO_FUNC_SPI));

    // Chip select is active-low, so we'll initialise it to a driven-high state
    gpio_init(SPI_MISO_CS);
    gpio_put(SPI_MISO_CS, 1);
    gpio_set_dir(SPI_MISO_CS, GPIO_OUT);

    reg_wizchip_cs_cbfunc(wizchip_cs_select_cbfunc, wizchip_cs_deselect_cbfunc);
    reg_wizchip_spi_cbfunc(wiznet_spi_rb_cbfunc, wizchip_spi_wb_cbfunc);

}
/* DHCP */
static void wizchip_dhcp_init(void);
static void wizchip_dhcp_assign(void);
static void wizchip_dhcp_conflict(void);
/* DHCP */
static uint8_t g_dhcp_get_ip_flag = 0;

/* Timer */
bool repeating_timer_callback(struct repeating_timer *t);

int main()
{
    /* Initialize */
    uint8_t retval = 0;
    uint8_t dhcp_retry = 0;
    stdio_init_all();
     sleep_ms(2000);
    gpio_config();
    // Enable SPI 0 at 8 MHz and co,nnect to GPIOs
    spi_init(SIP_ID, 8 * 1000 * 1000);
     struct repeating_timer timer;
    add_repeating_timer_ms(1000, repeating_timer_callback, NULL, &timer);
   wizchip_version = getVER();
        printf("wizchip_version = %02X\r\n", wizchip_version);
        sleep_ms(1000);
       if (net_info.dhcp == NETINFO_DHCP) // DHCP
    {
        wizchip_dhcp_init();
    }
   else // static
    {
        wizchip_setnetinfo(&net_info); // apply from DHCP
        printf("mac: %02X%02X%02X%02X%02X%02X\r\n", net_info.mac[0], net_info.mac[1], net_info.mac[2], net_info.mac[3], net_info.mac[4], net_info.mac[5]);
        printf("ip: %d.%d.%d.%d\r\n", net_info.ip[0], net_info.ip[1], net_info.ip[2], net_info.ip[3]);
        printf("mask: %d.%d.%d.%d\r\n", net_info.sn[0], net_info.sn[1], net_info.sn[2], net_info.sn[3]);
        printf("gw: %d.%d.%d.%d\r\n", net_info.gw[0], net_info.gw[1], net_info.gw[2], net_info.gw[3]);
    }

    while (1)
    {
       
          if (net_info.dhcp == NETINFO_DHCP)
        {
            retval = DHCP_run();

            if (retval == DHCP_IP_LEASED)
            {
                if (g_dhcp_get_ip_flag == 0)
                {
                    printf(" DHCP success\n");

                    g_dhcp_get_ip_flag = 1;
                }
            }
            else if (retval == DHCP_FAILED)
            {
                g_dhcp_get_ip_flag = 0;
                dhcp_retry++;

                if (dhcp_retry <= DHCP_RETRY_COUNT)
                {
                    printf(" DHCP timeout occurred and retry %d\n", dhcp_retry);
                }
            }

            if (dhcp_retry > DHCP_RETRY_COUNT)
            {
                printf(" DHCP failed\n");

                DHCP_stop();

                while (1)
                    ;
            }

             wiz_delay_ms(1000); // wait for 1 second
        }
    }
       
 
}
/**
 * ----------------------------------------------------------------------------------------------------
 * Functions
 * ----------------------------------------------------------------------------------------------------
 */

static void wizchip_dhcp_init(void)
{
    printf(" DHCP client running\n");

    DHCP_init(SOCKET_DHCP, g_ethernet_buf);

    reg_dhcp_cbfunc(wizchip_dhcp_assign, wizchip_dhcp_assign, wizchip_dhcp_conflict);
}
static void wizchip_dhcp_assign(void)
{
    getIPfromDHCP(net_info.ip);
    getGWfromDHCP(net_info.gw);
    getSNfromDHCP(net_info.sn);
    getDNSfromDHCP(net_info.dns);

    net_info.dhcp = NETINFO_DHCP;

    /* Network initialize */
    wizchip_setnetinfo(&net_info); // apply from DHCP
    printf("mac: %02X%02X%02X%02X%02X%02X\r\n", net_info.mac[0], net_info.mac[1], net_info.mac[2], net_info.mac[3], net_info.mac[4], net_info.mac[5]);
    printf("ip: %d.%d.%d.%d\r\n", net_info.ip[0], net_info.ip[1], net_info.ip[2], net_info.ip[3]);
    printf("mask: %d.%d.%d.%d\r\n", net_info.sn[0], net_info.sn[1], net_info.sn[2], net_info.sn[3]);
    printf("gw: %d.%d.%d.%d\r\n", net_info.gw[0], net_info.gw[1], net_info.gw[2], net_info.gw[3]);
    printf(" DHCP leased time : %ld seconds\n", getDHCPLeasetime());
}
static void wizchip_dhcp_conflict(void)
{
    printf(" Conflict IP from DHCP\n");

    // halt or reset or any...
    while (1)
        ; // this example is halt.
}

bool repeating_timer_callback(struct repeating_timer *t) {
    DHCP_time_handler();
    return true;
}